package org.minuframework.file;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.minuframework.lang.StringUtils;
import org.minuframework.utils.LangUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

public class PiecewiseFileUploader extends FileUploaderBase {
    
    private static Logger logger = LoggerFactory.getLogger(PiecewiseFileUploader.class);
    
    /**
     * 用户名
     */
    private String username;
    
    /**
     * 文件块
     */
    private String chunk;
    
    /**
     * 文件块大小
     */
    private String chunkSize;
    
    /**
     * 文件名
     */
    private String filename;
    
    /**
     * 文件MD5
     */
    private String fileMd5;
    
    /**
     * 临时文件
     */
    private File tempFile;
    
    public PiecewiseFileUploader(HttpServletRequest request) {
        if (DefaultFileUploader.isMultipartContent(request)) {
            this.request = (MultipartHttpServletRequest) request;
        }
    }
    
    public PiecewiseFileUploader(MultipartFile multipartFile, File target) {
        this.multipartFile = multipartFile;
        this.target = target;
    }
    
    public void write() {
        Map<String, MultipartFile> files = getMutipartHttpServletRequest(request).getFileMap();
        if (null != files && !files.isEmpty()) {
            for (MultipartFile item : files.values()) {
                // 创建分片文件并保存
                File chunkFile = new File(getTempDir(tempFile.toString(), username, filename, fileMd5));
                try {
                    FileUtils.isFile(chunkFile);
                    chunkFile = new File(chunkFile, chunk);
                    FileUtils.write(item, chunkFile);
                } catch (IllegalStateException e) {
                    logger.error("保存分片文件出错：" + e.getMessage());
                    e.printStackTrace();
                } catch (IOException e) {
                    logger.error("保存分片文件出错：" + e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }
    
    public String merge() {
        File tempDir = new File(getTempDir(tempFile.toString(), username, filename, fileMd5));
        // 创建目标文件夹
        File outfile = getOutFile();
        // 获得分片文件列表,转成集合进行排序后合并文件
        List<File> fileList = getFileList(tempDir);
        // 执行合并操作
        try {
            FileUtils.write(fileList, outfile);
            File tempFileDir = new File(tempFile.toString(), filename);
            FileUtils.delete(tempFileDir);
            return outfile.toString();
        } catch (IOException e) {
            logger.error("合并分片文件出错：" + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }
    
    public File getOutFile() {
        File outfile = this.target;
        try {
            outfile = buildPath(FileUtils.suffix(filename));
        } catch (IOException e) {
            logger.error("创建目标文件出错：" + e.getMessage());
            e.printStackTrace();
        }
        return outfile;
    }
    
    /**
     * 分片文件是否存在，尺寸是否一致
     * @return boolean
     */
    public boolean checkSize() {
        String tempDir = getTempDir(tempFile.toString(), filename, fileMd5, chunk);
        File chunkFile = new File(tempDir);
        return chunkFile.exists() && chunkFile.length() == LangUtils.parseInt(chunkSize);
    }
    
    public File buildPath(String suffixName) throws IOException {
        if (this.target.isFile()) {
            if (this.target.exists()) {
                target.createNewFile();
            }
            return target;
        }
        File folder = new File(target, FileUtils.getFolderByMonth());
        FileUtils.isFile(folder);
        return FileUtils.buildPath(folder, prefix, 0, suffixName);
    }
    
    /**
     * 获得分片文件目录集合
     * @param  dir 文件夹路径
     * @return     List<File>
     */
    public static List<File> getFileList(File dir) {
        File[] fileArray = getFileArray(dir);
        List<File> fileList = getFileList(fileArray);
        return fileList;
    }
    
    /**
     * 获得分片文件目录数组
     * @param  dir 文件夹路径
     * @return     File[]
     */
    public static File[] getFileArray(File dir) {
        File[] fileArray = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.isDirectory()) {
                    return false;
                } else {
                    return true;
                }
            }
        });
        return fileArray;
    }
    
    /**
     * 获得分片文件目录集合
     * @param  fileArray 分片文件目录
     * @return           List<File>
     */
    public static List<File> getFileList(File[] fileArray) {
        List<File> fileList = new ArrayList<File>(Arrays.asList(fileArray));
        Collections.sort(fileList, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if (LangUtils.parseInt(o1.getName()) < LangUtils.parseInt(o2.getName())) {
                    return -1;
                } else {
                    return 1;
                }
            }
        });
        return fileList;
    }
    
    /**
     * 获得分片文件临时保存路径
     * @param  tempPath 临时文件根目录
     * @param  userName 用户名
     * @param  filename 文件名
     * @param  fileMd5  文件MD5
     * @return          String 分片临时保存路径
     */
    public static String getTempDir(String tempPath, String userName, String filename, String fileMd5) {
        StringBuilder dir = new StringBuilder(tempPath);
        if (StringUtils.isNotEmpty(userName)) {
            dir.append(File.separator).append(userName);
        }
        dir.append(File.separator).append(filename);
        dir.append(File.separator).append(fileMd5);
        return dir.toString();
    }
    
    public PiecewiseFileUploader username(String username) {
        this.username = username;
        return this;
    }
    
    public PiecewiseFileUploader chunk(String chunk) {
        this.chunk = chunk;
        return this;
    }
    
    public PiecewiseFileUploader chunkSize(String chunkSize) {
        this.chunkSize = chunkSize;
        return this;
    }
    
    public PiecewiseFileUploader filename(String filename) {
        this.filename = filename;
        return this;
    }
    
    public PiecewiseFileUploader fileMd5(String fileMd5) {
        this.fileMd5 = fileMd5;
        return this;
    }
    
    public PiecewiseFileUploader setTempFile(File tempFile) {
        this.tempFile = tempFile;
        return this;
    }
    
}
